#include "VariableDeclaration.hpp"
#include "IfElement.hpp"
#include "AssignmentElement.hpp"
#include "Function.hpp"
#include "FunctionArgumentList.hpp"
#include "LiteralExpression.hpp"
#include "VariableExpression.hpp"
#include "FunctionExpression.hpp"
#include "FunctionNotFoundException.hpp"
#include "VariableAlreadyDefinedException.hpp"
#include "VariableNotFoundException.hpp"
#include "TypeMismatchException.hpp"
#include "Variable.hpp"
#include "Block.hpp"
#include "NullElement.hpp"
#include "ExpressionElement.hpp"
#include "ForElement.hpp"
#include "Program.hpp"
#include "TrustedCast.hpp"

#include <iostream>


/*
template<class BaseClass> class Dummy : public BaseClass
{
public:
@!for(int i=0; i<9; i+=1)
	@list("template<%>", "class T%", i)@ Dummy() : BaseClass("@{
		if(i % 2 == 0) "abc" elif(i % 3 == 0) "def" elif(i % 5 == 0) "ghi" else "xxxxx"
	}@") {} 
@!next
	~Dummy()
};
*/

#define LOCATION SourceLocation(__FILE__, __LINE__)

int main()
{
	int retVal = 0;
	try
	{
		ptr<Element> elem = new NullElement(LOCATION);
		{
			char const * str = 
				"template<class BaseClass> class Dummy : public BaseClass\n"
				"{\n"
				"public:\n";
			ptr<Value> val = new StrValue(str);
			ptr<Expression> expr = new LiteralExpression(LOCATION, val);
			elem = elem->concat(new ExpressionElement(LOCATION, expr));
		}
		{
			ptr<Variable> var = new Variable(LOCATION, "i", new LiteralExpression(LOCATION, new IntValue(0)));
			ptr<VariableDeclarationList> vars = new VariableDeclarationList();
			vars->addVariable(var);
			ptr<Element> init = new VariableDeclaration(LOCATION, Value::Int, vars);

			ptr<Expression> x1 = new VariableExpression(LOCATION, "i");
			ptr<Expression> x2 = new LiteralExpression(LOCATION, new IntValue(9));
			ptr<FunctionArgumentList> args = new FunctionArgumentList(LOCATION);
			args->addArgument(x1);
			args->addArgument(x2);
			ptr<Expression> cond = new FunctionExpression(LOCATION, "operator<", args);	

			ptr<Expression> x3 = new LiteralExpression(LOCATION, new IntValue(1));
			ptr<Element> step = new AssignmentElement(LOCATION, "i", "operator+=", x3);

			ptr<Element> body = new NullElement(LOCATION);
			ptr<Expression> xxx = new LiteralExpression(LOCATION, new StrValue("    "));
			body = body->concat(new ExpressionElement(LOCATION, xxx));

			ptr<Expression> ax1 = new LiteralExpression(LOCATION, new StrValue("template<%> "));
			ptr<Expression> ax2 = new LiteralExpression(LOCATION, new StrValue("class T%"));
			ptr<Expression> ax3 = new VariableExpression(LOCATION, "i");
			ptr<FunctionArgumentList> args2 = new FunctionArgumentList(LOCATION);
			args2->addArgument(ax1);
			args2->addArgument(ax2);
			args2->addArgument(ax3);
			ptr<Expression> xx = new FunctionExpression(LOCATION, "listx", args2);
			body = body->concat(new ExpressionElement(LOCATION, xx));
			ptr<Expression> xy = new LiteralExpression(LOCATION, new StrValue("class Dummy() : BaseClass(\""));
			body = body->concat(new ExpressionElement(LOCATION, xy));

			ptr<ElifBranch> b0 = new ElifBranch(
				LOCATION, 
				new FunctionExpression(
					LOCATION,
					"operator==",
					new FunctionArgumentList(
						LOCATION,
						new FunctionExpression(LOCATION, "operator%",
							new FunctionArgumentList(
								LOCATION,
								new VariableExpression(LOCATION, "i"),
								new LiteralExpression(LOCATION, new IntValue(2))
							)
						),
						new LiteralExpression(LOCATION, new IntValue(0))
					)
				),
				new ExpressionElement(LOCATION,
					new LiteralExpression(LOCATION, new StrValue("abc"))
				)
			);

			ptr<ElifBranch> b1 = new ElifBranch(
				LOCATION, 
				new FunctionExpression(
					LOCATION,
					"operator==",
					new FunctionArgumentList(
						LOCATION,
						new FunctionExpression(
							LOCATION,
							"operator%",
							new FunctionArgumentList(
								LOCATION,
								new VariableExpression(LOCATION, "i"),
								new LiteralExpression(LOCATION, new IntValue(3))
							)
						),
						new LiteralExpression(LOCATION, new IntValue(0))
					)
				),
				new ExpressionElement(LOCATION,
					new LiteralExpression(LOCATION, new StrValue("def"))
				)
			);

			ptr<ElifBranch> b2 = new ElifBranch(
				LOCATION, 
				new FunctionExpression(
					LOCATION,
					"operator==",
					new FunctionArgumentList(
						LOCATION,
						new FunctionExpression(
							LOCATION,
							"operator%",
							new FunctionArgumentList(
								LOCATION,
								new VariableExpression(LOCATION, "i"),
								new LiteralExpression(LOCATION, new IntValue(5))
							)
						),
						new LiteralExpression(LOCATION, new IntValue(0))
					)
				),
				new ExpressionElement(LOCATION,
					new LiteralExpression(LOCATION, new StrValue("ghi"))
				)
			);

			ptr<Element> belse = new ExpressionElement(LOCATION,
				new LiteralExpression(LOCATION, new StrValue("xxxxx"))
			);

			ptr<ElifBranchList> blist = new ElifBranchList();
			blist->addBranch(b1);
			blist->addBranch(b2);

			body = body->concat(new IfElement(LOCATION, b0, blist, belse));
		
			ptr<Expression> xz = new LiteralExpression(LOCATION, new StrValue("\") {}\n"));
			body = body->concat(new ExpressionElement(LOCATION, xz));
			
			elem = elem->concat(new ForElement(LOCATION, init, cond, step, body));
		}
		{
			char const * str = 
				"	~Dummy() {}\n"
				"};\n";
			ptr<Expression> expr = new LiteralExpression(LOCATION, new StrValue(str));
			elem = elem->concat(new ExpressionElement(LOCATION, expr));
		}

		Program program(LOCATION, elem, &std::cout);
		program.execute();

		ptr<Object> obj = new NullElement(LOCATION);
		ptr<NullElement> p1 = trusted_cast(obj);
		ptr<Program> p2 = trusted_cast(obj);

		retVal = 0;
	}
	catch(Exception const & ex)
	{
		std::cerr << ex.what();
		retVal = 1;
	}
	assert(Object::allObjectsRemoved());
	return retVal;
}
